#include "./include/ringbuffer.h"
#include "./include/basictype.h"
#include "./include/sys_dbg.h"
#include <string.h>
// TODO:
//    1. 暂时未考虑中断的影响
//    2. 读取和写入暂时不支持异步方式

void *RingBuffer_Init(RingBuffer_t *ringBuf, UINT8 *buf, UINT32 bufSize) {
  SYS_ASSERT(ringBuf != NULL);
  SYS_ASSERT(buf != NULL);
  SYS_ASSERT(bufSize > 0);

  ringBuf->start = buf;
  ringBuf->end = buf + bufSize - 1;
  ringBuf->lpos = buf;
  ringBuf->rpos = buf;

  return (void *)ringBuf;
}

INT32 RingBuffer_Size(RingBuffer_t *ringBuf) {
  SYS_ASSERT(ringBuf != NULL);

  return (ringBuf->end - ringBuf->start);
}

INT32 RingBuffer_DataLen(RingBuffer_t *ringBuf) {
  SYS_ASSERT(ringBuf != NULL);

  if (ringBuf->lPos <= ringBuf->rpos) {
    return (ringBuf->rpos - ringBuf->lPos);
  } else {
    INT32 leftSize = ringBuf->rpos - ringBuf->start;
    INT32 rightSize = ringBuf->end - ringBuf->lpos + 1;
    return (leftSize + rightSize);
  }
}

// RingBuffer的剩余空间大小
INT32 RingBuffer_SpaceLen(RingBuffer_t *ringBuf) {
  SYS_ASSERT(ringBuf != NULL);
  return (RingBuffer_Size(ringBuf) - RingBuffer_DataLen(ringBuf));
}

static INT32 RingBuffer_copyBlock(UINT8 *pDst, UINT8 *pSrc, UINT8 *pSrcLimit) {
  SYS_ASSERT(pDst != NULL);
  SYS_ASSERT(pSrc != NULL);
  SYS_ASSERT(pSrcLimit != NULL);
  SYS_ASSERT(pSrc < pSrcLimit);

  INT32 copyLen = 0;

  while (pSrc < pSrcLimit) {
    *pDst++ = *pSrc++;
    copyLen++;
  }
  return copyLen;
}

INT32 RingBuffer_Read(RingBuffer_t *ringBuf, UINT8 *buf, INT32 toReadSize) {
  SYS_ASSERT(ringBuf != NULL);
  SYS_ASSERT(buf != NULL);

  if (!toReadSize)
    return 0;

  INT32 dataLen = RingBuffer_DataLen(ringBuf);
  if (!dataLen)
    return 0;

  if (toReadSize > dataLen)
    toReadSize = dataLen;

  if (ringBuf->lpos < ringBuf->rpos) {
    memcpy(buf, ringBuf->lpos, toReadSize);
    ringBuf->lpos = ringBuf->lpos + toReadSize;
    return toReadSize;
  } else {
    INT32 copyLen;
    if ((ringBuf->end - ringBuf->lpos + 1) >= toReadSize) {
      copyLen = RingBuffer_copyBlock(buf, ringBuf->lpos, ringBuf->lpos + toReadSize);
      ringBuf->lpos += copyLen;
      return copyLen;
    }

    copyLen = RingBuffer_copyBlock(buf, ringBuf->lpos, ringBuf->end + 1);
    toReadSize -= copyLen;
    buf += copyLen;

    copyLen += RingBuffer_copyBlock(buf, ringBuf->start, ringBuf->start + toReadSize);
    ringBuf->lpos = ringBuf->start + toReadSize;

    return copyLen;
  }
}

INT32 RingBuffer_Peek(RingBuffer_t *ringBuf, UINT8 *buf, INT32 peekSize){
  SYS_ASSERT(ringBuf != NULL);
  SYS_ASSERT(buf != NULL);

  if (!peekSize)
    return 0;

  INT32 dataLen = RingBuffer_DataLen(ringBuf);
  if (!dataLen)
    return 0;

  if (peekSize > dataLen)
    peekSize = dataLen;

  if (ringBuf->lpos < ringBuf->rpos) {
    memcpy(buf, ringBuf->lpos, peekSize);
    return peekSize;
  } else {
    INT32 copyLen;
    if ((ringBuf->end - ringBuf->lpos + 1) >= peekSize) {
      return  RingBuffer_copyBlock(buf, ringBuf->lpos, ringBuf->lpos + peekSize);
    }

    copyLen = RingBuffer_copyBlock(buf, ringBuf->lpos, ringBuf->end + 1);
    peekSize -= copyLen;
    buf += copyLen;

    copyLen += RingBuffer_copyBlock(buf, ringBuf->start, ringBuf->start + peekSize);
    return copyLen;
  }
}

INT32 RingBuffer_Write(RingBuffer_t *ringBuf, UINT8 *buf, INT32 toWriteSize) {
  SYS_ASSERT(ringBuf != NULL);
  SYS_ASSERT(buf != NULL);

  if (!toWriteSize)
    return 0;

  INT32 spaceLen = RingBuffer_SpaceLen(ringBuf);
  if(!spaceLen)
    return 0;

  if (toWriteSize > spaceLen)
    toWriteSize = spaceLen;
  
  INT32 copyLen;
  if(ringBuf->lpos > ringBuf->rpos){
    copyLen = RingBuffer_copyBlock(ringBuf->rpos, buf, buf + toWriteSize);
    ringBuf->rpos += ringBuf->rpos + copyLen;
    if(ringBuf->rpos>ringBuf->end)
      ringBuf->rpos=ringBuf->start;
    return copyLen;
  }else{
    INT32 rLeftSize = ringBuf->end-ringBuf->rpos+1;
    if(rLeftSize >= toWriteSize){
      copyLen = RingBuffer_copyBlock(ringBuf->rpos, buf, buf + toWriteSize);
      ringBuf->rpos += ringBuf->rpos + toWriteSize;

      if(ringBuf->rpos > ringBuf->end)
        ringBuf->rpos = ringBuf->start;
      return copyLen;
    }

    copyLen = RingBuffer_copyBlock(ringBuf->rpos, buf, buf + rLeftSize);
    buf += rLeftSize; 
    toWriteSize -= copyLen;

    copyLen += RingBuffer_copyBlock(ringBuf->start, buf, buf + toWriteSize);
    ringBuf->rpos = ringBuf->start + toWriteSize;
    if(ringBuf->rpos>ringBuf->end)
      ringBuf->rpos=ringBuf->start;
    return copyLen;
  }
}

void RingBuffer_Reset(RingBuffer_t *ringBuf){
  SYS_ASSERT(ringBuf != NULL);
  ringBuf->start = NULL;
  ringBuf->end = NULL;
  ringBuf->lpos = NULL;
  ringBuf->rpos = NULL;
}